/*-------------------------------------------------------------------------
 *
 * arch-x86.h
 *	  特定于 intel x86 的原子操作考虑
 *
 * 请注意，我们实际上要求至少使用 486，因为 386 不支持 xadd 和 cmpxchg。
 * 鉴于 386 在任何地方都不再受支持，这并不是很大的限制。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * NOTES:
 *
 * src/include/port/atomics/arch-x86.h
 *
 *-------------------------------------------------------------------------
 */

/*
 * x86 的 32 位和 64 位都不允许加载与其他加载重排序，
 * 或存储与其他存储重排序，但可以在随后的存储之前执行一个加载。
 *
 * 在技术上，一些类 x86 的芯片支持不缓存内存访问和/或
 * 特殊指令，这些指令是弱序的。在这些情况下，我们需要
 * 读取和写入屏障为 lfence 和 sfence。但由于我们不做这些事情，
 * 编译器屏障应该足够了。
 *
 * "lock; addl" 的效果早于 "mfence"。据说在许多场景中也更快。
 */

#if defined(__GNUC__) || defined(__INTEL_COMPILER)
#if defined(__i386__) || defined(__i386)
#define pg_memory_barrier_impl()		\
	__asm__ __volatile__ ("lock; addl $0,0(%%esp)" : : : "memory", "cc")
#elif defined(__x86_64__)
#define pg_memory_barrier_impl()		\
	__asm__ __volatile__ ("lock; addl $0,0(%%rsp)" : : : "memory", "cc")
#endif
#endif /* defined(__GNUC__) || defined(__INTEL_COMPILER) */

#define pg_read_barrier_impl()		pg_compiler_barrier_impl()
#define pg_write_barrier_impl()		pg_compiler_barrier_impl()

/*
 * 在 x86 gcc 上使用内联汇编提供原子的实现。支持旧版 gcc 是
 * 很好的，这里的比较/交换实现实际上比 * __sync 变体更有效。
 */
#if defined(HAVE_ATOMICS)

#if defined(__GNUC__) || defined(__INTEL_COMPILER)

#define PG_HAVE_ATOMIC_FLAG_SUPPORT
typedef struct pg_atomic_flag
{
	volatile char value;
} pg_atomic_flag;

#define PG_HAVE_ATOMIC_U32_SUPPORT
typedef struct pg_atomic_uint32
{
	volatile uint32 value;
} pg_atomic_uint32;

/*
 * 在 32 位上为 64 位类型编写内联 asm 太复杂，并且 486 也无法做到。
 */
#ifdef __x86_64__
#define PG_HAVE_ATOMIC_U64_SUPPORT
typedef struct pg_atomic_uint64
{
	/* 由于在 64 位平台上，因此保证对齐 */
	volatile uint64 value;
} pg_atomic_uint64;
#endif	/* __x86_64__ */

#endif /* defined(__GNUC__) || defined(__INTEL_COMPILER) */

#endif /* defined(HAVE_ATOMICS) */

#if !defined(PG_HAVE_SPIN_DELAY)
/*
 * 这个序列相当于 PAUSE 指令（"rep" 在旧的 IA32 处理器中被忽略，
 * 如果后面的指令不是字符串操作）；IA-32 架构软件
 * 开发者手册，卷 3，第 7.7.2 节描述了在自旋锁的内部循环中使用
 * PAUSE 的原因，以获得良好的性能：
 *
 *     PAUSE 指令改善了支持超线程技术的 IA-32
 *     处理器在执行自旋等待循环和其他例程时的性能，
 *     其中一个线程在紧密轮询循环中访问共享锁或信号量。
 *     当执行自旋等待循环时，处理器在退出循环时可能会遭受严重的
 *     性能惩罚，因为它检测到可能的内存顺序违反并刷新核心处理器的流水线。
 *     PAUSE 指令向处理器提供了一个提示，表明代码序列是自旋等待循环。
 *     处理器使用这个提示来避免内存顺序违反并防止流水线刷新。
 *     此外，PAUSE 指令还会使自旋等待循环去流水线，以防止其
 *     过度消耗执行资源。
 */
#if defined(__GNUC__) || defined(__INTEL_COMPILER)
#define PG_HAVE_SPIN_DELAY
static __inline__ void
pg_spin_delay_impl(void)
{
	__asm__ __volatile__(" rep; nop			\n");
}
#elif defined(_MSC_VER) && defined(__x86_64__)
#define PG_HAVE_SPIN_DELAY
static __forceinline void
pg_spin_delay_impl(void)
{
	_mm_pause();
}
#elif defined(_MSC_VER)
#define PG_HAVE_SPIN_DELAY
static __forceinline void
pg_spin_delay_impl(void)
{
	/* 请参阅 gcc 代码的注释。相同的代码，MASM 语法 */
	__asm rep nop;
}
#endif
#endif /* !defined(PG_HAVE_SPIN_DELAY) */


#if defined(HAVE_ATOMICS)

#if defined(__GNUC__) || defined(__INTEL_COMPILER)

#define PG_HAVE_ATOMIC_TEST_SET_FLAG
static inline bool
pg_atomic_test_set_flag_impl(volatile pg_atomic_flag *ptr)
{
	register char _res = 1;

	__asm__ __volatile__(
		"	lock			\n"
		"	xchgb	%0,%1	\n"
:		"+q"(_res), "+m"(ptr->value)
:
:		"memory");
	return _res == 0;
}

#define PG_HAVE_ATOMIC_CLEAR_FLAG
static inline void
pg_atomic_clear_flag_impl(volatile pg_atomic_flag *ptr)
{
	/*
	 * 在像 x86 这样的 TSO 架构中，使用编译器
	 * 屏障即可实现释放语义。
	 */
	__asm__ __volatile__("" ::: "memory");
	ptr->value = 0;
}

#define PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U32
static inline bool
pg_atomic_compare_exchange_u32_impl(volatile pg_atomic_uint32 *ptr,
									uint32 *expected, uint32 newval)
{
	char	ret;

	/*
	 * 执行 cmpxchg 并使用它隐式设置为零的标志
	 * 来测量成功。
	 */
	__asm__ __volatile__(
		"	lock				\n"
		"	cmpxchgl	%4,%5	\n"
		"   setz		%2		\n"
:		"=a" (*expected), "=m"(ptr->value), "=q" (ret)
:		"a" (*expected), "r" (newval), "m"(ptr->value)
:		"memory", "cc");
	return (bool) ret;
}

#define PG_HAVE_ATOMIC_FETCH_ADD_U32
static inline uint32
pg_atomic_fetch_add_u32_impl(volatile pg_atomic_uint32 *ptr, int32 add_)
{
	uint32 res;
	__asm__ __volatile__(
		"	lock				\n"
		"	xaddl	%0,%1		\n"
:		"=q"(res), "=m"(ptr->value)
:		"0" (add_), "m"(ptr->value)
:		"memory", "cc");
	return res;
}

#ifdef __x86_64__

#define PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64
static inline bool
pg_atomic_compare_exchange_u64_impl(volatile pg_atomic_uint64 *ptr,
									uint64 *expected, uint64 newval)
{
	char	ret;

	/*
	 * 执行 cmpxchg 并使用它隐式设置为零的标志
	 * 来测量成功。
	 */
	__asm__ __volatile__(
		"	lock				\n"
		"	cmpxchgq	%4,%5	\n"
		"   setz		%2		\n"
:		"=a" (*expected), "=m"(ptr->value), "=q" (ret)
:		"a" (*expected), "r" (newval), "m"(ptr->value)
:		"memory", "cc");
	return (bool) ret;
}

#define PG_HAVE_ATOMIC_FETCH_ADD_U64
static inline uint64
pg_atomic_fetch_add_u64_impl(volatile pg_atomic_uint64 *ptr, int64 add_)
{
	uint64 res;
	__asm__ __volatile__(
		"	lock				\n"
		"	xaddq	%0,%1		\n"
:		"=q"(res), "=m"(ptr->value)
:		"0" (add_), "m"(ptr->value)
:		"memory", "cc");
	return res;
}

#endif /* __x86_64__ */

#endif /* defined(__GNUC__) || defined(__INTEL_COMPILER) */

/*
 * 8 字节读/写在至少 586 的 32 位 x86 平台上
 * 具有单副本原子性。对于所有 x86-64 CPU 也是如此。
 */
#if defined(__i568__) || defined(__i668__) || /* gcc i586+ */  \
	(defined(_M_IX86) && _M_IX86 >= 500) || /* msvc i586+ */ \
	defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) /* gcc, sunpro, msvc */
#define PG_HAVE_8BYTE_SINGLE_COPY_ATOMICITY
#endif /* 8 字节单副本原子性 */

#endif /* HAVE_ATOMICS */
